﻿using Common.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Wicresoft.EKA.CommonExtensions;

namespace Wicresoft.EKA.NRemedy
{
    public class ARLoginContextAdminPool : ICleanUp
    {

        private static readonly ILog logger = LogManager.GetLogger(typeof(ARLoginContextAdminPool));

        private static ARLoginContextAdminPool _instance;

        private static readonly Object lockObject = new Object();

        private Dictionary<string, ARLoginContextAdminAndLastUsedTime> items;

        private ARLoginContextAdminPool()
        {
            items = new Dictionary<string, ARLoginContextAdminAndLastUsedTime>();
            CleanUpThread cleanThread = new CleanUpThread(this, this._sleepTime);
            Thread t = new Thread(new ThreadStart(cleanThread.run));
            t.Start();
        }

        public string Server { get; set; }
        public string AdminUser { get; set; }
        public string AdminPassword { get; set; }
        public string AdminAuthentication { get; set; }

        public static ARLoginContextAdminPool Instance
        {
            get
            {
                if (_instance != null) return _instance;
                lock (lockObject)
                {
                    if (_instance == null)
                    {
                        _instance = new ARLoginContextAdminPool();
                    }
                    return _instance;
                }
            }
        }

        public IARServer CreateARServer()
        {
            return new ARServer();
        }

        public void Return(string key)
        {
            //lock (items)
            //{
            logger.Info("Return " + key + "  " + items.Count);
            items[key].IsInUse = false;
            items[key].LastUsedTime = DateTime.Now;
            //Monitor.Pulse(items);
            //}
        }


        public ARLoginContext GetLoginContext(out string key)
        {
            var resultNo = items.Where(o => o.Value.IsInUse == false);
            if (resultNo.Count() > 0)
            {
                var result = resultNo.FirstOrDefault();
                result.Value.IsInUse = true;
                result.Value.LastUsedTime = DateTime.Now;
                key = result.Key;
                return result.Value.LogInContext;
            }

            lock (items)
            {
                logger.Info("GetLoginContext " + "  " + items.Count);
                if (items.Count(o => o.Value.IsInUse == false) > 0)
                {
                    logger.Info("GetLoginContext 0" + "  " + items.Count);
                    var result = items.Where(o => o.Value.IsInUse == false).FirstOrDefault();
                    result.Value.IsInUse = true;
                    result.Value.LastUsedTime = DateTime.Now;
                    key = result.Key;
                    return result.Value.LogInContext;
                }

                int count = items.Keys.Count;
                if (count < this._poolSize || 0 == _poolSize)
                {
                    logger.Info("GetLoginContext  1" + "  " + items.Count);
                    string keyGe = Guid.NewGuid().ToString();
                    var loginContext = new ARLoginContext(Server, AdminUser, AdminPassword, new ARServerFactoryDefault(), AdminAuthentication);
                    items.Add(keyGe, new ARLoginContextAdminAndLastUsedTime()
                    {
                        LogInContext = loginContext,
                        LastUsedTime = DateTime.Now,
                        IsInUse = true
                    });

                    key = keyGe;
                    return loginContext;
                }
                else
                {
                    int i = 0;
                    while (true)
                    {
                        if (i > 2)
                        {
                            throw new InvalidOperationException("Theard Error in the ARLoginContextAdminPool");
                            //break;
                        };
                        Monitor.Wait(items);

                        logger.Info("GetLoginContext Wait " + "  " + items.Count);

                        if (items.Count(o => o.Value.IsInUse == false) > 0)
                        {

                            var result = items.Where(o => o.Value.IsInUse == false).FirstOrDefault();
                            result.Value.IsInUse = true;
                            result.Value.LastUsedTime = DateTime.Now;
                            key = result.Key;
                            logger.Info("GetLoginContext  3" + "  " + key + "  " + items.Count);
                            return result.Value.LogInContext;
                        }

                        int count1 = items.Keys.Count;
                        if (count1 < this._poolSize || 0 == _poolSize)
                        {
                            logger.Info("GetLoginContext  4" + "  " + items.Count);
                            string keyGe = Guid.NewGuid().ToString();
                            var loginContext = new ARLoginContext(Server, AdminUser, AdminPassword, new ARServerFactoryDefault(), AdminAuthentication);
                            items.Add(keyGe, new ARLoginContextAdminAndLastUsedTime()
                            {
                                LogInContext = loginContext,
                                LastUsedTime = DateTime.Now,
                                IsInUse = true
                            });

                            key = keyGe;
                            logger.Info("GetLoginContext  4" + "  " + key + "  " + items.Count);
                            return loginContext;
                        }
                        i++;
                    }
                }
            }
        }

        private int _expirationTime = 2 * 60 * 60 * 1000;

        public int ExpirationTime
        {
            get
            {
                return this._expirationTime;
            }
            set
            {
                this._expirationTime = value;
            }
        }

        private int _sleepTime = 5 * 60 * 1000;

        public int SleepTime
        {
            get
            {
                return this._sleepTime;
            }
            set
            {
                this._sleepTime = value;
            }
        }

        private int _poolSize = 0;

        public int PoolSize
        {
            get
            {
                return this._poolSize;
            }
            set
            {
                this._poolSize = value;
            }
        }

        public void Clean()
        {
            lock (items)
            {
                var expires = items.Where(i => (i.Value.LastUsedTime.AddMilliseconds(_expirationTime)) <= (DateTime.Now)).ToList();
                foreach (var item in expires)
                {
                    ARLoginContextAdminAndLastUsedTime asa = item.Value;
                    if (!asa.IsInUse && asa.LastUsedTime.AddMilliseconds(_expirationTime) <= DateTime.Now)
                    {
                        items.Remove(item.Key);
                        asa.Dispose();
                        logger.Info("Clean  3" + "  " + item.Key + "  " + items.Count);
                    }
                    Monitor.Pulse(items);
                    logger.Info("Pulse  3" + "  " + item.Key + "  " + items.Count);
                }
            }
        }


    }
}
